package cn.zxd.pub;


import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * Created by filon51 on 2016/3/14.
 */
public class ReflectionUtil {
//    public static void objectToMap(Object source, Map target)
//    {
//        try
//        {
//            target.putAll(ClassInvokeUtil.initCommonDTO(source));
//        }
//        catch (Exception e) {
//            throw new RuntimeException("fail to  mapToObject!");
//        }
//    }
//
//    public static void mapToObject(Map source, Object target)
//    {
//        try
//        {
//            ClassInvokeUtil.objectToMap(source, target);
//        }
//        catch (Exception e) {
//            throw new RuntimeException("fail to  objectToMap!");
//        }
//    }

    public static void copyProperties(Object source, Object target) {
        copyProperties(source, target, null);
    }

    public static void copyProperties(Object source, Object target, String[] ignoreProperties) {
        if ((null == source) || (null == target)) {
            return;
        }
        Set excludes = new HashSet();
        if (null != ignoreProperties) {
            excludes.addAll(Arrays.asList(ignoreProperties));
        }
        copyProperties(source, target, excludes, false);
    }

    private static void copyProperties(Object source, Object target, Set properties, boolean included) {
        try {
            BeanInfo sourceInfo = Introspector.getBeanInfo(source.getClass());
            Map targetDescrs = new HashMap();

            BeanInfo targetInfo = Introspector.getBeanInfo(target.getClass());
            PropertyDescriptor[] targetPds = targetInfo.getPropertyDescriptors();
            for (int i = 0; i < targetPds.length; ++i) {
                targetDescrs.put(targetPds[i].getName(), targetPds[i]);
            }

            PropertyDescriptor[] pds = sourceInfo.getPropertyDescriptors();
            Object[] params = new Object[1];

            for (int i = 0; i < pds.length; ++i) {
                String property = pds[i].getName();
                if (included) {
                    if (properties.contains(property)) ;
                } else {
                    if (properties.contains(property)) {
                        continue;
                    }

                    PropertyDescriptor targetPD = (PropertyDescriptor) targetDescrs.get(property);
                    if (null != targetPD) {
                        Method readMethod = pds[i].getReadMethod();
                        Method writeMethod = targetPD.getWriteMethod();
                        if (null == readMethod) continue;
                        if (null == writeMethod) {
                            continue;
                        }
                        try {
                            Class[] ps = writeMethod.getParameterTypes();
                            if ((ps != null) && (ps.length == 1)) {
                                Object propSrc = readMethod.invoke(source, null);
                                params[0] = convertIfNeeded(ps[0], propSrc, pds[i].getName());
                                writeMethod.invoke(target, params);
                            }
                        } catch (IllegalArgumentException e) {
                        } catch (IllegalAccessException e) {
                        } catch (InvocationTargetException e) {
                        }
                    }
                }
            }

        } catch (IntrospectionException e) {
            handleEx(e);
        }
    }

    public static void setProperty(Object target, String property, Object value) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(target.getClass());
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < pds.length; ++i) {
                if (pds[i].getName().equals(property)) {
                    Method method = pds[i].getWriteMethod();
                    if (null == method) {
                        throw new IllegalArgumentException("No WriteMethod of property: " + property);
                    }
                    Class[] ps = method.getParameterTypes();
                    if (ps == null) continue;
                    if (ps.length != 1) {
                        continue;
                    }
                    Object[] params = new Object[1];

                    params[0] = convertIfNeeded(ps[0], value, pds[i].getName());
                    method.invoke(target, params);

                    return;
                }
            }
            throw new IllegalArgumentException("No Such property: " + property);
        } catch (IntrospectionException e) {
            handleEx(e);
        } catch (IllegalAccessException e) {
            handleEx(e);
        } catch (InvocationTargetException e) {
            handleEx(e);
        }

        throw new RuntimeException("No Such property: " + property);
    }

    private static Object convertIfNeeded(Class type, Object value, String property) {
        if (null != value) {
            if (value instanceof Number) {
                return convertNumber(type, (Number) value);
            }
            return value;
        }

        if ((Integer.class == type) || (Integer.TYPE == type)) {
            return new Integer(0);
        }
        if ((Short.class == type) || (Short.TYPE == type)) {
            return new Short("0");
        }
        if ((Long.class == type) || (Long.TYPE == type)) {
            return new Long(0L);
        }
        if ((Float.class == type) || (Float.TYPE == type)) {
            return new Float(0.0F);
        }
        if ((Double.class == type) || (Double.TYPE == type)) {
            return new Double(0.0D);
        }
        if ((Byte.class == type) || (Byte.TYPE == type)) {
            return new Integer(0);
        }
        if ((Character.class == type) || (Character.TYPE == type)) {
            return new Character('\0');
        }
        if ((Boolean.class == type) || (Boolean.TYPE == type)) {
            return Boolean.FALSE;
        }

        return value;
    }

    private static Number convertNumber(Class destType, Number value) {
        if (destType == BigDecimal.class) {
            return new BigDecimal(value.toString());
        }
        if (destType == BigInteger.class) {
            return new BigInteger(value.toString());
        }
        if ((destType == Long.class) || (destType == Long.TYPE)) {
            return new Long(value.longValue());
        }
        if ((destType == Integer.class) || (destType == Integer.TYPE)) {
            return new Integer(value.intValue());
        }
        if ((destType == Short.class) || (destType == Short.TYPE)) {
            return new Short(value.shortValue());
        }
        if ((destType == Float.class) || (destType == Float.TYPE)) {
            return new Float(value.floatValue());
        }
        if ((destType == Double.class) || (destType == Double.TYPE)) {
            return new Double(value.doubleValue());
        }
        if ((destType == Byte.class) || (destType == Byte.TYPE)) {
            return new Byte(value.byteValue());
        }

        return value;
    }

    private static void handleEx(IntrospectionException ex) {
        throw new RuntimeException("Access Error", ex);
    }

    private static void handleEx(IllegalAccessException ex) {
        throw new RuntimeException("Access Error", ex);
    }

    private static void handleEx(InvocationTargetException ex) {
        throw new RuntimeException("Invocation Error", ex);
    }
}
